package cn.ex.ymcc.service.impl;


import cn.ex.ymcc.doc.CourseDoc;
import cn.ex.ymcc.mapper.HighlightResultMapper;
import cn.ex.ymcc.repository.CourseRepository;
import cn.ex.ymcc.result.PageList;
import cn.ex.ymcc.service.ICourseESService;
import cn.ex.ymcc.util.AssertUtil;
import cn.ex.ymcc.vo.AggrsBucket;
import cn.ex.ymcc.vo.CourseQueryDto;
import cn.ex.ymcc.vo.EsAggsPageList;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class CourseESServiceImpl implements ICourseESService {

    @Autowired
    private CourseRepository courseRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    private HighlightResultMapper highlightResultMapper;

    /**
     * 保存课程到es
     * @param doc
     */
    @Override
    public void onLineCourse(CourseDoc doc) {
        courseRepository.save(doc);
    }

    /**
     * 从es下架课程
     * @param courseId
     */
    @Override
    public void offLineCourse(Long courseId) {
        CourseDoc courseDoc = courseRepository.findById(courseId).get();

        AssertUtil.isNotNull(courseDoc,"课程信息不存在");
        //删除相关的es内容
        courseRepository.deleteById(courseId);
    }


    /**
     * es 搜索课程
     * @param dto
     * @return
     */
    @Override
    public PageList<CourseDoc> searchCourse(CourseQueryDto dto) {
        //获取多条件查询的方法
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //设置分页条件PageRequest
        queryBuilder.withPageable(PageRequest.of(dto.getPage() - 1, dto.getRows()));
        //设置排序规则
        setSortAndPaging(dto,queryBuilder) ;
        //封装查询条件
        BoolQueryBuilder boolQuery = setQueryParam(dto, queryBuilder);
        //设置高亮`和 聚合
        PageList<CourseDoc> courseDocPageList = setHighlightAndAggS(queryBuilder, boolQuery);
        //返回数据
        return courseDocPageList;
    }



    /**
     * 设置高亮
     * @param queryBuilder
     */
    private PageList<CourseDoc> setHighlightAndAggS(NativeSearchQueryBuilder queryBuilder,BoolQueryBuilder query) {
        //设置查询字段的高亮样式
        queryBuilder.withHighlightFields(
                new HighlightBuilder.Field("name")
                        .preTags("<font style='color:red'>").postTags("</font>"));
        //等级 聚合（分组并统计数量）
        queryBuilder.addAggregation(AggregationBuilders.terms("gradeNameAgg").field("gradeName"));
        // 收费规则聚合  terms： 传递参数————>聚合名  filed:传递参数————> es存储的字段名
        queryBuilder.addAggregation(AggregationBuilders.
                terms("chargeNameAgg").
                field("chargeName").
                order(BucketOrder.count(false)));
        //执行查询，搜索es
        queryBuilder.withQuery(query);
        //获取 NativeSearchQuery来进行查询高亮
        NativeSearchQuery build = queryBuilder.build();
        //聚合类page接口
        AggregatedPage<CourseDoc> page = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);
        //获取所有的聚合结果
        Map<String, Aggregation> aggsMap = page.getAggregations().asMap();
        //处理结果json序列化
        ParsedStringTerms gradeNameAgg = (ParsedStringTerms)aggsMap.get("gradeNameAgg");
        ParsedStringTerms chargeNameAgg = (ParsedStringTerms)aggsMap.get("chargeNameAgg");

        Map<String, List<AggrsBucket>> resultMap = new HashMap<>();
        // List<? extends Terms.Bucket>  --> List<Aggs>
        //等级聚合结果处理
        List<AggrsBucket> gradeNameAggList = gradeNameAgg.getBuckets().stream()
                .map(bucket -> new AggrsBucket(bucket.getKeyAsString(), bucket.getDocCount()))
                .collect(Collectors.toList());
        //收费规则聚合结果处理
        List<AggrsBucket> chargeNameAggList = chargeNameAgg.getBuckets().stream()
                .map(bucket -> new AggrsBucket(bucket.getKeyAsString(), bucket.getDocCount()))
                .collect(Collectors.toList());

        resultMap.put("gradeNameAgg",gradeNameAggList);
        resultMap.put("chargeNameAgg",chargeNameAggList);

        return new EsAggsPageList<>(page.getTotalElements(),page.getContent(), resultMap);


    }

    /**
     * 封装查询条件
     * @param dto
     * @param queryBuilder
     */
    private BoolQueryBuilder setQueryParam(CourseQueryDto dto, NativeSearchQueryBuilder queryBuilder) {

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //must :课程名，关键字查询
        if(StringUtils.hasLength(dto.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name", dto.getKeyword()));
            //boolQuery.must(QueryBuilders.multiMatchQuery(dto.getKeyword(),"name","tenantName"));
        }
        //filter :
        //课程类型
        if(dto.getCourseTypeId() != null){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId", dto.getCourseTypeId()));
        }
        //等级名
        if(StringUtils.hasLength(dto.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName", dto.getGradeName()));
        }
        //最小价格
        if(null != dto.getPriceMin()){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(dto.getPriceMin()));
        }
        //最大价格
        if(null != dto.getPriceMax()){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(dto.getPriceMax()));
        }
        return boolQuery;
    }

    /**
     * 设置排序规则
     * @param dto
     * @param queryBuilder
     */
    private void setSortAndPaging(CourseQueryDto dto, NativeSearchQueryBuilder queryBuilder) {

        if(StringUtils.hasLength(dto.getSortField())){
            //默认以上架时间排序
            String sortField = "";
            switch (dto.getSortField().toLowerCase()){
                case "xl":sortField = "saleCount";break;
                case "xp":sortField = "onlineTime";break;
                case "pl":sortField = "commentCount";break;
                case "jg":sortField = "price";break;
                case "rq":sortField = "viewCount";break;
                //默认为按照上架时间排序
                default: sortField = "onlineTime";
            }
            //默认升序
            SortOrder sortOrder = SortOrder.ASC;
            if(StringUtils.hasLength(dto.getSortType())){
                sortOrder = dto.getSortType().equalsIgnoreCase("ASC") ? SortOrder.ASC : SortOrder.DESC;
            }
            //排序
            queryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortOrder));
        }
    }
}
